import { MessageStyle, useMessage } from "@/hooks/message";
import { AxiosResponse } from "axios";
import { ZAxiosOption } from ".";
import { isObjectLike } from "..";
import { FieldConfig } from "./config";
import { isGetMethod } from "./helpers";

export interface ZResponseData<R = unknown> {
  code: number;
  message?: string;
  result?: R;
}

export interface ZResponsePageDataResult<T = unknown> {
  list: T[];
  total: number;
}

export interface ZResponsePageData<T = unknown> extends ZResponseData {
  result: ZResponsePageDataResult<T>;
}

export type ZResponseRawData = Record<string, any>;

export function transformResponse<R = unknown>(
  response: AxiosResponse<ZResponseRawData>,
  option: Omit<ZAxiosOption, "config"> & { fieldConfig: Required<FieldConfig> }
): [
  0 | 1, // 0 for resolve; 1 for reject
  AxiosResponse<ZResponseRawData> | ZResponseRawData | R | any | undefined,
] {
  const {
    fieldConfig,
    responseCodeConfig,
    preserveNativeResponse,
    preserveResponseData,
    messageStyle,
  } = option;

  if (preserveNativeResponse) return [0, response];

  const { config, data } = response;

  if (preserveResponseData || !isObjectLike(data)) return [0, data];

  const { code, message, result } = transData<R>(data, fieldConfig);
  const { zTip } = useMessage();

  let msgStyle = messageStyle;

  if (code === responseCodeConfig!.OK) {
    // successful operations (usually of which ${config.method} is not 'GET') would be prompted, if not specified
    if (msgStyle === undefined) {
      if (!isGetMethod(config.method)) {
        msgStyle = MessageStyle.message;
      } else {
        msgStyle = MessageStyle.none;
      }
    }

    zTip(message, msgStyle, "success");

    return [0, result];
  } else {
    // errors would always be prompted, if not specified
    if (msgStyle === undefined) {
      msgStyle = MessageStyle.message;
    }

    zTip(message, msgStyle, "warning");

    // catch ${code} for special handling
    return [1, code];
  }
}

/**
 * transform custom ${response.data} to ${ZResponseData}
 */
function transData<R = unknown>(
  data: ZResponseRawData,
  fieldConfig: Required<FieldConfig>
): ZResponseData<R> {
  const { codeField, msgField, resultField } = fieldConfig;

  return {
    code: data[codeField],
    message: data[msgField],
    result: data[resultField],
  };
}

// TODO: extra works for ${data.result}
/**
 * transform custom ${data.result} to ${ZResponsePageDataResult}
 */
function transResult<R = unknown>(
  result: ZResponseRawData,
  fieldConfig: Required<FieldConfig>
): ZResponsePageDataResult<R> {
  const { listField, totalField } = fieldConfig;

  return {
    list: result[listField],
    total: result[totalField],
  };
}
